using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Ferpect.GameComponents
{
    public class ViewComponent : Component, IDrawable, IGameComponent, IUpdateable
    {
        // Fields
        private IGraphicsDeviceService deviceService;
        private bool initialized;
        private ContentManager contentManager;
        private int drawOrder;
        private bool visible;
        private bool enabled;
        private int updateOrder;

        private EventHandler drawOrderChanged;
        private EventHandler visibleChanged;
        private EventHandler enabledChanged;
        private EventHandler updateOrderChanged;

        public ViewComponent()
        {
            this.visible = true;
            this.enabled = true;
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual ContentManager Content
        {
            get
            {
                return this.contentManager;
            }
            set
            {
                this.contentManager = value;
            }
        }

        [Browsable(false)]
        public bool Initialized
        {
            get { return this.initialized; }
        }

        protected virtual void LoadContent()
        {
        }

        protected virtual void UnloadContent()
        {
        }

        /// <summary>
        /// Used by this component's designer to unload and reload
        /// </summary>
        public virtual void Reinitialize()
        {
            if (this.initialized)
            {
                this.UnloadContent();
                this.LoadContent();
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GraphicsDevice GraphicsDevice
        {
            get
            {
                if (this.deviceService == null)
                {
                    throw new InvalidOperationException("Property cannot be called before Initialize.");
                }
                return this.deviceService.GraphicsDevice;
            }
        }

        #region Component overrides

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.UnloadContent();
                if (this.deviceService != null)
                {
                    this.deviceService.DeviceCreated -= new EventHandler(this.DeviceCreated);
                    this.deviceService.DeviceDisposing -= new EventHandler(this.DeviceDisposing);
                }
            }
            base.Dispose(disposing);
        }

        protected override object GetService(Type service)
        {
            ContentManager content = this.Content;
            if (content != null)
            {
                object serviceInstance = content.ServiceProvider.GetService(service);
                if (serviceInstance != null)
                {
                    return serviceInstance;
                }
            }
            return base.GetService(service);
        }

        #endregion

        #region IDrawable

        public virtual void Draw(GameTime gameTime)
        {
        }

        [Category("Behavior")]
        [Description("The order in which to draw this object relative to other objects. Objects with a lower value are drawn first.")]
        [DefaultValue(0)]
        public int DrawOrder
        {
            get
            {
                return this.drawOrder;
            }
            set
            {
                if (this.drawOrder != value)
                {
                    this.drawOrder = value;
                    this.OnDrawOrderChanged(this, EventArgs.Empty);
                }
            }
        }

        public event EventHandler DrawOrderChanged
        {
            add
            {
                this.drawOrderChanged += value;
            }
            remove
            {
                this.drawOrderChanged -= value;
            }
        }

        protected virtual void OnDrawOrderChanged(object sender, EventArgs args)
        {
            if (this.drawOrderChanged != null)
            {
                this.drawOrderChanged(this, args);
            }
        }

        [Category("Behavior")]
        [Description("Determines whether the component should be drawn. When false, the Draw method will not be called.")]
        [DefaultValue(true)]
        public bool Visible
        {
            get
            {
                return this.visible;
            }
            set
            {
                if (this.visible != value)
                {
                    this.visible = value;
                    this.OnVisibleChanged(this, EventArgs.Empty);
                }
            }
        }

        public event EventHandler VisibleChanged
        {
            add
            {
                this.visibleChanged += value;
            }
            remove
            {
                this.visibleChanged -= value;
            }
        }

        protected virtual void OnVisibleChanged(object sender, EventArgs args)
        {
            if (this.visibleChanged != null)
            {
                this.visibleChanged(this, args);
            }
        }

        #endregion

        #region IGameComponent

        public virtual void Initialize()
        {
            if (!this.initialized)
            {
                this.deviceService = this.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
                if (this.deviceService == null)
                {
                    throw new InvalidOperationException("Unable to initialize - unable to obtain IGraphicsDeviceService.");
                }
                this.deviceService.DeviceCreated += new EventHandler(this.DeviceCreated);
                this.deviceService.DeviceDisposing += new EventHandler(this.DeviceDisposing);
                if (this.deviceService.GraphicsDevice != null)
                {
                    this.LoadContent();
                }
            }
            this.initialized = true;
        }

        private void DeviceCreated(object sender, EventArgs e)
        {
            this.LoadContent();
        }

        private void DeviceDisposing(object sender, EventArgs e)
        {
            this.UnloadContent();
        }

        #endregion

        #region IUpdateable Members

        [Category("Behavior")]
        [Description("Indicates whether the component should be updated. If false, the Update method will not be called.")]
        [DefaultValue(true)]
        public bool Enabled
        {
            get
            {
                return this.enabled;
            }
            set
            {
                if (this.enabled != value)
                {
                    this.enabled = value;
                    this.OnEnabledChanged(this, EventArgs.Empty);
                }
            }
        }

        public event EventHandler EnabledChanged
        {
            add
            {
                this.enabledChanged += value;
            }
            remove
            {
                this.enabledChanged -= value;
            }
        }

        protected virtual void OnEnabledChanged(object sender, EventArgs args)
        {
            if (this.enabledChanged != null)
            {
                this.enabledChanged(this, args);
            }
        }

        public virtual void Update(GameTime gameTime)
        {
        }

        [Category("Behavior")]
        [Description("Indicates when the game component should be updated relative to other game components. Lower values are updated first.")]
        [DefaultValue(0)]
        public int UpdateOrder
        {
            get
            {
                return this.updateOrder;
            }
            set
            {
                if (this.updateOrder != value)
                {
                    this.updateOrder = value;
                    this.OnUpdateOrderChanged(this, EventArgs.Empty);
                }
            }
        }

        public event EventHandler UpdateOrderChanged
        {
            add
            {
                this.updateOrderChanged += value;
            }
            remove
            {
                this.updateOrderChanged -= value;
            }
        }

        protected virtual void OnUpdateOrderChanged(object sender, EventArgs args)
        {
            if (this.updateOrderChanged != null)
            {
                this.updateOrderChanged(this, args);
            }
        }

        #endregion
    }
}
